package com.education.platform.api.service.impl.common;

import com.education.platform.api.config.ThreadUser;
import com.education.platform.api.constants.RedisCacheConstant;
import com.education.platform.api.service.ArticleService;
import com.education.platform.api.service.CourseService;
import com.education.platform.api.service.common.CacheDataService;
import com.education.platform.api.service.entity.LoginSuccessDTO;
import com.education.platform.api.vo.request.ChallengeResultVO;
import com.education.platform.api.vo.response.CollectOrLikeListResponseVO;
import com.education.platform.common.enums.YesOrNoTypeEnum;
import com.education.platform.common.exception.BusinessException;
import com.education.platform.common.exception.ParameterException;
import com.education.platform.common.redis.RedisUtils;
import com.education.platform.common.service.entity.PageDTO;
import com.education.platform.common.util.StringUtils;
import com.education.platform.dao.entity.ArticleDO;
import com.education.platform.dao.entity.CourseDO;
import com.education.platform.dao.entity.UserBaseDO;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Tuple;

import java.text.NumberFormat;
import java.util.*;

/**
 * 缓存服务
 * 点赞
 * 收藏
 * 积分--积分赞直接保存数据库
 *
 * @author tomato
 */
@Service
public class CacheDataServiceImpl implements CacheDataService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private Integer challengeTimeSum = 10;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private CourseService courseService;

    @Autowired
    private ArticleService articleService;

    private final NumberFormat nf= NumberFormat.getNumberInstance() ;

    /**
     * 初始化用户计数器
     * 收藏数，点赞数，累计学习时长，今日学习时长
     */
    @Override
    public void initRedisCount(Integer userId) {
        Map<String, String> cacheMap = new HashMap<>(10);
        cacheMap.put(RedisCacheConstant.FIELD_COLLECTION, "0");
        cacheMap.put(RedisCacheConstant.FIELD_LIKE, "0");

        boolean result = redisUtils.hmsetStr(getCounterRedisKey(userId), cacheMap);

        logger.info("初始化用户计数器：" + result);
    }

    /**
     * 初始化今日学习时间
     *
     * @param
     */
    @Override
    public void initRedisTodayCount() {
        //每日登录
        redisUtils.del(RedisCacheConstant.HASH_KEY_DAILY_LOGIN);
        //每日答题
        redisUtils.del(RedisCacheConstant.HASH_KEY_DAILY_ANSWER);
        //每日学习视频
        redisUtils.del(RedisCacheConstant.HASH_KEY_DAILY_LEARN_VIDEO);
        ///每日学习文章
        redisUtils.del(RedisCacheConstant.HASH_KEY_DAILY_LEARN_ARTICLE);
        //每日分享
        redisUtils.del(RedisCacheConstant.HASH_KEY_DAILY_SHARE);
        //每日挑战次数
        redisUtils.del(RedisCacheConstant.HASH_KEY_DAILY_CHALLENGE_NUM);
    }

    /**
     * 获取用户计数器统计数据
     *
     * @param dto
     */
    @Override
    public void getUserRedisCount(LoginSuccessDTO dto) {
        if (dto == null) {
            return;
        }
        Integer userId = ThreadUser.getUserId();
        String counterKey = getCounterRedisKey(userId);
        if (!redisUtils.exists(counterKey)) {
            this.initRedisCount(userId);
        }

        if (redisUtils.hexists(counterKey, RedisCacheConstant.FIELD_COLLECTION)) {
            dto.setCollectionCount(Integer.valueOf(redisUtils.hgetStr(counterKey, RedisCacheConstant.FIELD_COLLECTION)));
        } else {
            dto.setCollectionCount(0);
        }
        if (redisUtils.hexists(counterKey, RedisCacheConstant.FIELD_LIKE)) {
            dto.setLikeCount(Integer.valueOf(redisUtils.hgetStr(counterKey, RedisCacheConstant.FIELD_LIKE)));
        } else {
            dto.setLikeCount(0);
        }
    }


    /**
     * 获取用户计数器 key  --- hash
     *
     * @param userId
     * @return
     */
    private String getCounterRedisKey(Integer userId) {
        return RedisCacheConstant.KEY_COUNTER + userId;
    }

    /**
     * 获取用户收藏记录key  ---hash
     *
     * @param userId
     * @return
     */
    private String getCollectionRedisKey(Integer userId) {
        return RedisCacheConstant.KEY_COLLECTION + userId;
    }

    /**
     * 获取用户点赞记录key  ---hash
     *
     * @param userId
     * @return
     */
    private String getLikeRedisKey(Integer userId) {
        return RedisCacheConstant.KEY_LIKE + userId;
    }


    /**
     *  挑战用时排序
     * @return
     */
    private String getUserChallengeRedisKey() {
        return RedisCacheConstant.KEY_CHALLENGE_SECONDS;
    }

    @Override
     public ChallengeResultVO userChallenge(Integer numbers) {
        ChallengeResultVO resultVO = new ChallengeResultVO();
        resultVO.setCurrentNumber(numbers);
        String key = this.getUserChallengeRedisKey();
        UserBaseDO user = ThreadUser.getUser(UserBaseDO.class);
        //记录用户自己的历史最高答题数目，和之前的比较，覆盖
        Double score = redisUtils.zscore(key, String.valueOf(user.getId()));
        if (score == null || score.compareTo(numbers.doubleValue()) == -1) {
            redisUtils.zaddStr(key, numbers, String.valueOf(user.getId()));
            resultVO.setMostNumber(numbers);
        } else {
            resultVO.setMostNumber(score.intValue());
        }

        //获取剩余挑战次数，答题前已有接口增加了次数，这里不需要处理
        String n = redisUtils.hgetStr(RedisCacheConstant.HASH_KEY_DAILY_CHALLENGE_NUM, String.valueOf(user.getId()));
        if (n != null) {
            resultVO.setSurplusTimes(challengeTimeSum - Integer.valueOf(n) < 0 ? 0 : challengeTimeSum - Integer.valueOf(n));
        }
        //本次答对题目数量的排名百分比
        redisUtils.zaddStr(RedisCacheConstant.KEY_CHALLENGE_NUMBER, numbers, String.valueOf(user.getId()));
        if (numbers != null && numbers > 0) {
            Long total = redisUtils.zcard(RedisCacheConstant.KEY_CHALLENGE_NUMBER);
            Long rank = redisUtils.zrevrank(RedisCacheConstant.KEY_CHALLENGE_NUMBER, String.valueOf(user.getId()));
            if (total == null || total == 0) {
                resultVO.setProportion(1.00f);
            } else if (rank == null) {
                resultVO.setProportion(0);
            } else {
                nf.setMaximumFractionDigits(2);
                resultVO.setProportion(Float.valueOf(nf.format((total - rank) / (float) total)));
            }
        }else{
            resultVO.setProportion(0);
        }
        logger.info("提交挑战答题结果："+resultVO);
        return resultVO;
     }


    /**
     * 收藏
     *  包含   课程和文章
     * @param topicId
     * @param status
     * @param  topicType  0=课程收藏  1=文章收藏
     * @param
     */
    @Override
    public void collectOperate(Integer topicId, Boolean status,int topicType ) {
        Integer userId = ThreadUser.getUserId();
        if (topicId == null || userId == null) {
            throw new ParameterException("收藏失败");
        }

        //保存收藏关系
        String field  = topicType==1?(RedisCacheConstant.ARTICLE_FLAG+topicId):String.valueOf(topicId);

        String currentState = redisUtils.hgetStr(getCollectionRedisKey(userId),field);

        this.check(currentState,status,false);

        redisUtils.hsetStr(getCollectionRedisKey(userId), field, status ? YesOrNoTypeEnum.Y.getValue() : YesOrNoTypeEnum.N.getValue());
        String counterKey = getCounterRedisKey(userId);
        //用户counter收藏数+1
        redisUtils.hincrBy(counterKey, RedisCacheConstant.FIELD_COLLECTION, status?1:-1);

        //主题 被收藏数+1
        String key =  topicType==1?RedisCacheConstant.KEY_ARTICLE_COLLECT:RedisCacheConstant.KEY_COURSE_COLLECT;
        if (redisUtils.hexists(key, String.valueOf(topicId))) {
            redisUtils.hincrBy(key, String.valueOf(topicId), status?1:-1);
        } else {
            redisUtils.hsetStr(key, String.valueOf(topicId), "1");
        }

    }

    /**
     * 点赞操作
     * 课程和文章
     * @param topicId
     * @param status
     * @param topicType  0=课程点赞  1=文章点赞
     */
    @Override
    public void likeOperate(Integer topicId, Boolean status,int topicType) {
        Integer userId = ThreadUser.getUserId();
        if (topicId == null || userId == null) {
            throw new ParameterException("点赞失败");
        }
        //保存点赞关系
        String field  = topicType==1?(RedisCacheConstant.ARTICLE_FLAG+topicId):String.valueOf(topicId);
        String currentState = redisUtils.hgetStr(getLikeRedisKey(userId),field);

        this.check(currentState,status,true);

        redisUtils.hsetStr(getLikeRedisKey(userId), field, status ? YesOrNoTypeEnum.Y.getValue() : YesOrNoTypeEnum.N.getValue());

        String counterKey = getCounterRedisKey(userId);
        //用户点赞数+1
        redisUtils.hincrBy(counterKey, RedisCacheConstant.FIELD_LIKE, status?1:-1);
        //主题  被点赞数+1
        String key =  topicType==1?RedisCacheConstant.KEY_ARTICLE_LIKE:RedisCacheConstant.KEY_COURSE_LIKE;
        if (redisUtils.hexists(key, String.valueOf(topicId))) {
            redisUtils.hincrBy(key, String.valueOf(topicId), status?1:-1);
        } else {
            redisUtils.hsetStr(key, String.valueOf(topicId), "1");
        }
    }


    private void check(String currentState,boolean status,boolean operateLike){

        String errMsg = operateLike?"点赞!":"收藏!";

        if(StringUtils.isEmpty(currentState)){
            if(!status){
                throw new BusinessException("当前未"+errMsg);
            }
        }else{
            if(YesOrNoTypeEnum.Y.getValue().equals(currentState)&&status){
                throw new BusinessException("当前已"+errMsg);
            }else if(YesOrNoTypeEnum.N.getValue().equals(currentState)&&!status){
                throw new BusinessException("当前未"+errMsg);
            }
        }

    }



    /**
     * 用户积分操作
     *1=每日登录 上限 1分
     * 2=挑战答题  上限 6分
     * 3=学习时长  上限 5分    没有用了
     * 4=学习课外知识  上限2分  没有用了
     * 5=学习视频     没有限制  需要记录
     * 6=学习文章     没有限制  需要记录
     * 7= 分享视频或文章  每日上限5分
     * @param score
     */
    @Override
    public float userPointsOperate(float score,Integer type) {
        Integer userId = ThreadUser.getUserId();
        String key = null;
        String field="" ;
        Float limit=null;
        switch (type){
            case 1:key = RedisCacheConstant.HASH_KEY_DAILY_LOGIN;
                limit=1f;
                break;
            case 2:key = RedisCacheConstant.HASH_KEY_DAILY_ANSWER;
                limit = 6f;
                break;

                //  3 和 4 没有用了
//            case 3:field = RedisCacheConstant.POINT_LEARN_TIME;
//                limit = 5f;
//                break;
//            case 4:field = RedisCacheConstant.POINT_OUTER_LEARN;
//                limit = 2f;
//                break;
            case 5:key = RedisCacheConstant.HASH_KEY_DAILY_LEARN_VIDEO;
                break;
            case 6:key = RedisCacheConstant.HASH_KEY_DAILY_LEARN_ARTICLE;
                break;
            case 7:key = RedisCacheConstant.HASH_KEY_DAILY_SHARE;
                limit = 5f;
                break;
        }
        String sum = redisUtils.hgetStr(key,String.valueOf(userId));
        sum= sum ==null?"0":sum;
        float finalIncreScore=0;
        if(limit != null && Float.valueOf(sum).equals(limit)){
            //不抛出异常
            logger.info("type="+type+"  积分已达上限 ");
            return  0;
        }
        Float  newScore =  Float.valueOf(sum)+score;
        if(limit != null && newScore.compareTo(limit)==1){
            newScore = limit;
            finalIncreScore = limit - Float.valueOf(sum);
        }else{
            finalIncreScore = score;
        }
        // 记录对应的  当日得分
        redisUtils.hsetStr(key,String.valueOf(userId),String.valueOf(newScore));
        // 实际  增加的分数
        return finalIncreScore;
    }


    /**
     * 获取用户收藏/点赞 列表
     * 目前仅查询  课程  列表
     *  type  1=收藏  2=点赞
     *  topicType  0=课程 1=文章
     * @return
     */
    @Override
    public PageDTO<CollectOrLikeListResponseVO> getUserCollectionOrLikeList(int type, int topicType, int pageNo, int pageSize) {
        PageDTO<CollectOrLikeListResponseVO> pageDTO= new PageDTO<>();
        List<CollectOrLikeListResponseVO> vos = new ArrayList<>();
        pageDTO.setPageSize(pageSize);
        pageDTO.setPageNum(pageNo);
        Integer userId = ThreadUser.getUserId();
        String redisKey = null;
        switch (type) {
            case 1:
                redisKey = getCollectionRedisKey(userId);
                break;
            case 2:
                redisKey = getLikeRedisKey(userId);
                break;
            default:
                throw new BusinessException("未知类型");
        }

        Map<String, String> map = redisUtils.hgetAllStr(redisKey);

        //获取收藏或者点赞的对象id
        List<Integer> topicIds = new ArrayList<>();
        List<Integer> pageTopicIds = new ArrayList<>();
        if(topicType==0){
            map.forEach((k, v) -> {
                if (YesOrNoTypeEnum.Y.getValue().equals(v)) {
                    if(Integer.valueOf(k)>0){
                        topicIds.add(Integer.valueOf(k));
                    }
                }
            });
        }else{
            map.forEach((k, v) -> {
                if (YesOrNoTypeEnum.Y.getValue().equals(v)) {
                    if(Integer.valueOf(k)<0){
                        topicIds.add(Integer.valueOf(k)*-1);
                    }
                }
            });
        }

        int total = topicIds.size();
        pageDTO.setTotal((long)total);
        if(total >0 ){
            int start;
            int end;
            start = pageSize * (pageNo - 1);
            end = pageSize + start;
            if(start<total){
                if(end>total ){
                    end = total;
                }
                pageTopicIds=  topicIds.subList(start,end);
            }
        }

        if(CollectionUtils.isNotEmpty(pageTopicIds)){
            if(topicType==0) {
                List<CourseDO> courseDOS = courseService.getListByIds(pageTopicIds);
                courseDOS.parallelStream().forEach(m -> {
                    CollectOrLikeListResponseVO vo = new CollectOrLikeListResponseVO();
                    vo.setId(m.getId());
                    vo.setName(m.getCourseName());
                    vo.setCoverImage(m.getCourseImage());
                    vo.setDescription(m.getDescription());
                    vo.setViewNumbers(m.getViewsNumber());
                    vos.add(vo);
                });
            }else{
                List<ArticleDO> articleDOS = articleService.getListByIds(pageTopicIds);
                articleDOS.parallelStream().forEach(m -> {
                    CollectOrLikeListResponseVO vo = new CollectOrLikeListResponseVO();
                    vo.setId(m.getId());
                    vo.setName(m.getTitle());
                    vo.setCoverImage(m.getCover());
                    vo.setDescription(m.getOutline());
                    vo.setViewNumbers(m.getViewNumber());
                    vos.add(vo);
                });
            }
        }
        pageDTO.setList(vos);
        return pageDTO;
    }



    /**
     * 获取用户收藏/点赞  状态
     *
     * @return
     */
    @Override
    public String getCollectionStatus(Integer topicId,int topicType) {
        Integer userId = ThreadUser.getUserId();
        String redisKey = getCollectionRedisKey(userId);
        String field  = topicType==1?(RedisCacheConstant.ARTICLE_FLAG+topicId):String.valueOf(topicId);
        String status = redisUtils.hgetStr(redisKey,field);
        return StringUtils.isNotEmpty(status) ? status : YesOrNoTypeEnum.N.getValue();
    }

    /**
     * 获取点赞状态
     *
     * @param
     * @return
     */
    @Override
    public String getLikeStatus(Integer topicId,int topicType) {
        Integer userId = ThreadUser.getUserId();
        String redisKey = getLikeRedisKey(userId);
        String field  = topicType==1?(RedisCacheConstant.ARTICLE_FLAG+topicId):String.valueOf(topicId);
        String status = redisUtils.hgetStr(redisKey,field);
        return StringUtils.isNotEmpty(status) ? status : YesOrNoTypeEnum.N.getValue();
    }

    @Override
    public Integer getCourseCollectCount(Integer courseId) {
        String num = redisUtils.hgetStr(RedisCacheConstant.KEY_COURSE_COLLECT, String.valueOf(courseId));
        if (StringUtils.isEmpty(num)) {
            return 0;
        }
        return Integer.valueOf(num);
    }

    @Override
    public Integer getCourseLikeCount(Integer courseId) {
        String num = redisUtils.hgetStr(RedisCacheConstant.KEY_COURSE_LIKE, String.valueOf(courseId));
        if (StringUtils.isEmpty(num)) {
            return 0;
        }
        return Integer.valueOf(num);
    }



    /**
     * 获取剩余挑战次数
     * @return
     */
    @Override
    public Integer getChallengeTime() {
        String n = redisUtils.hgetStr(RedisCacheConstant.HASH_KEY_DAILY_CHALLENGE_NUM,String.valueOf(ThreadUser.getUserId()));
        if(StringUtils.isEmpty(n)){
            return challengeTimeSum;
        }
        int s = challengeTimeSum-Integer.valueOf(n);
        return s<0?0:s;
    }


    @Override
    public void saveQueryKeyWord(String word) {
        if(StringUtils.isEmpty(word)){
            return;
        }
        redisUtils.zincrby(RedisCacheConstant.KEY_HOT_WORD,1,word);
    }

    @Override
    public List<String> getQueryKeyWordList(Integer num) {
        if(num == null||num==0){
            num=8;
        }
        List<String> list = new ArrayList<>(num);

        Set<Tuple> ranks = redisUtils.zrevrangeWithScores(RedisCacheConstant.KEY_HOT_WORD,0,num-1);
        if(CollectionUtils.isNotEmpty(ranks)){
            for(Tuple t: ranks){
                list.add(t.getElement());
            }
        }
        if(CollectionUtils.isEmpty(list)){
            list.add("试卷");
            list.add("历史");
        }
        return  list;
    }
}
